**Final Project** (##) **Claire Oudea (f00813g)** (#) Motivational Image ***Ship on Stormy Seas by Ivan Aivazovsky***
Ship on Stormy Seas
(#) Features
  • In file: pathTraceVolumeMIS.cpp
  • (##) Parellization with SmallThreadPool Material sampling ref Material sampling mine
        
        const int num_threads = std::max(1u, std::thread::hardware_concurrency());
        std::vector> samplers;
        samplers.reserve(num_threads);
    
        for (int i = 0; i < num_threads; ++i) samplers.push_back(m_sampler->clone());
    
        std::atomic rows_done{0};
        std::vector threads;
        threads.reserve(num_threads);
    
        auto worker = [&](int tid) {
            auto &local_sampler = *samplers[tid];
            thread_local std::mt19937 rng(std::random_device{}() + tid);
            std::uniform_real_distribution dist(0.0f, 1.0f);
    
            int rows_per_thread = (H + num_threads - 1) / num_threads;
            int y0 = tid * rows_per_thread;
            int y1 = std::min(H, y0 + rows_per_thread);
    
            for (int y = y0; y < y1; ++y) {
                for (int x = 0; x < W; ++x) {
                    Color3f pixel_color(0.0f);
                    // local_sampler.set_base_seed(randf());
                    for (int s = 0; s < m_num_samples; ++s) {
                        float rx = dist(rng);
                        float ry = dist(rng);
                        Vec2f pixel_sample(x + rx, y + ry);
                        Ray3f ray = m_camera->generate_ray(pixel_sample);
                        if (m_integrator)
                            pixel_color += m_integrator->Li(*this, local_sampler, ray);
                        else
                            pixel_color += recursive_color(ray, 0);
                    }
                    pixel_color /= float(m_num_samples);
                    image(x,y) = pixel_color;
                }
                int done = ++rows_done;
                progress.step(); 
            }
        };
        
    
    (##) Discovery Cluster Material sampling ref (##) Simple extra geometry: Cube Reference-pdf Reference-pdf (##) Environment Map Emitter (with importance sampling)
        
        if (!scene.intersect(ray_recursive, hit)) {
            // Iterate over all infinite emitters (env map)
            SurfaceGroup infinite_emitters = scene.infiniteEmitters();
            for (int i=0; i < infinite_emitters.child_num(); ++i) {
                auto infinite_emitter = infinite_emitters.child_by_index(i);
                float misWeight_mats; 
                if (depth == 0 || srec.is_specular)
                    misWeight_mats = 1.f;
                else {
                    float pdf_ems = scene.emitters().child_prob() * infinite_emitter->pdf(ray_recursive.o, ray_recursive.d);
                    misWeight_mats = pdf_mats / (pdf_mats + pdf_ems);
                }
                L += misWeight_mats * throughput * infinite_emitter->eval(ray_recursive.o, ray_recursive.d, hit);
                Color3f printtestcolor = infinite_emitter->eval(ray_recursive.o, ray_recursive.d, hit);
                spdlog::info("infinite_emitter->eval: {} {} {}", printtestcolor[0], printtestcolor[1], printtestcolor[2]);
            }
            break;
        }
        
    
        
        // box blur
        int radius = int(blur);
        Color3f sum(0.0f);
        int count = 0;
    
        for (int dx = -radius; dx <= radius; dx++)
        {
            for (int dy = -radius; dy <= radius; dy++)
            {
                int x = clamp(cx + dx, 0, image.width() - 1);
                int y = clamp(cy + dy, 0, image.height() - 1);
    
                sum += image.at(x, y);
                count++;
            }
        }
        return sum / float(count);
        
    
  • Based off the wikipedia page and other sources
  •     
        Color3f bilinear(float u, float v) const 
        {
            float x = u * (image.width() - 1);
            float y = (1.0f - v) * (image.height() - 1);
    
            int x0 = floor(x);
            int x1 = std::min(x0 + 1, image.width() - 1);
            int y0 = floor(y);
            int y1 = std::min(y0 + 1, image.height() - 1);
    
            float tx = x - x0;
            float ty = y - y0;
    
            Color3f c00 = image.at(x0, y0);
            Color3f c10 = image.at(x1, y0);
            Color3f c01 = image.at(x0, y1);
            Color3f c11 = image.at(x1, y1);
    
            return (1 - tx) * (1 - ty) * c00
                + tx      * (1 - ty) * c10
                + (1 - tx) * ty      * c01
                + tx      * ty      * c11;
        }
    
        Color3f bicubic(float u, float v) const
        {
            float x = u * (image.width() - 1);
            float y = (1.0f - v) * (image.height() - 1);
    
            int ix = floor(x);
            int iy = floor(y);
    
            Color3f sum(0.0f);
            float wsum = 0.0f;
    
            for (int m = -1; m <= 2; m++)
            {
                float wy = cubic_weight(float(iy + m) - y);
                int yy = clamp(iy + m, 0, image.height() - 1);
                for (int n = -1; n <= 2; n++)
                {
                    float wx = cubic_weight((ix + n) - x);
                    int xx = clamp(ix + n, 0, image.width() - 1);
                    float w = wx * wy;
                    sum += image.at(xx, yy) * w;
                    wsum += w;
                }
            }
            return sum / wsum;
        }
        
    
    Reference-pdf Reference-pdf (##) Captured Environment Map
    Reference-pdf
    (##) Heterogenous Volume Path Tracer Material sampling ref (##) Final Rendered Image
    Current Clouds Done in Blender (proof of concept)
    Current Clouds (##) Challenges My biggest challenge was debugging my heterogenous volume path tracer. At some point I generated a very artistic but also very "wrong" cloud. Since it looked nice, I rendered an artistic version of my final image below: ***Early Prototype***
    Prototype done in Blender